import { Node } from "cc";
import { Subscriber, Subscription } from "rxjs";

export function nullifySubscription(key) {
    if (this[key] != null) {
        this[key].unsubscribe();
        this[key] = null;
    }
}

export function deleteSubscription(key) {
    if (this[key] != null) {
        this[key].unsubscribe();
        delete this[key];
    }
}

/** 
 * Subscription
 */
declare module "./internal/Subscription" {
    interface Subscription {
        addTo(target: Subscription | Node): Subscription;
    }
}

/** subscriptionA.add(subscriptionB) => subscriptionB.addTo(subscriptionA) */
Object.defineProperty(Subscription.prototype, 'addTo', {
    value: function (target) {
        if (target instanceof Subscription) {
            target.add(this);
        } else if (target instanceof Node) {
            if (!target?.isValid) this.unsubscribe();
            else {
                let _subscriptions = target.attr['_subscriptions'] as Set<Subscription>;
                if (_subscriptions == null) {
                    _subscriptions = new Set<Subscription>();
                    target.attr({ _subscriptions });
                }
                _subscriptions.add(this);
                target.on(Node.EventType.NODE_DESTROYED, _ => {
                    target.clearSubscriptions();
                });
            }
        }
        return this;
    },
    writable: true,
    configurable: true,
});

declare module "cc" {
    interface Node {
        clearSubscriptions: () => void
    }
}

Node.prototype.clearSubscriptions = function () {
    let _subscriptions = this?.['_subscriptions'] as Set<Subscription>;
    if (_subscriptions != null) {
        for (let sub of _subscriptions) {
            sub.unsubscribe();
        }
        _subscriptions.clear();
        delete this['_subscriptions'];
    }
}

declare module "./internal/Subscriber" {
    interface Subscriber<T> {
        nextAndComplete(value?: T): void;
    }
}

Object.defineProperty(Subscriber.prototype, 'nextAndComplete', {
    value: function (value) {
        this.next(value);
        this.complete();
    }
});